package cn.deschen.common.config.security;

import cn.deschen.common.api.CmRes;
import cn.deschen.common.api.CmResCode;
import cn.deschen.common.utils.JwtTokenUtil;
import cn.deschen.common.utils.NoUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author hanbin_chen
 * @Description jwt认证过滤器
 * @DateTime 2021/6/3
 * @Version V1.0.0
 */
@Slf4j
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JwtProperties jwt;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserDetailsService userDetailsService;

    private static final String TRANSID = "transId";

    @Override
    protected void doFilterInternal(
            HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        log.debug("CALL API REQ = {}", request);
        setTransIdInLog(request);
        UserDetails userDetails = null;
        try {
            // 从请求头中获取认证信息, 内容 格式：“Bearer token”
            String authHeader = request.getHeader(jwt.getTokenHeader());
            // 判断请求头信息是否存在且其中token是否存在
            if (null != authHeader && authHeader.startsWith(jwt.getTokenHead())) {
                // 获取token信息
                String authToken = authHeader.substring(jwt.getTokenHead().length());
                // 判断token是否过期
                String userName = jwtTokenUtil.getUserNameFromToken(authToken);
                userDetails = userDetailsService.loadUserByUsername(userName);
                // 判断token是否有效
                if (jwtTokenUtil.validateToken(authToken, userDetails)) {
                    UserInfoContext.setCurrentUser(userDetails);
                }
            }
            filterChain.doFilter(request, response);
        } catch (Exception ex) {
            handler(request, response, ex, CmResCode.FORBIDDEN);
        } finally {
            UserInfoContext.setCurrentUser(userDetails);
        }

    }

    private void handler(HttpServletRequest request, HttpServletResponse response,
                         Exception ex, CmResCode resCode) throws IOException {
        response.setStatus(HttpStatus.OK.value());
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Cache-Control", "no-cache");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        response.getWriter().println(JSONUtil.parse(CmRes.failure(resCode)));
        response.getWriter().flush();
    }

    /**
     * 在日志中添加链路追踪的标识
     *
     * @param request
     */
    private void setTransIdInLog(HttpServletRequest request) {
        // 优先从头部读取，再从请求属性中读取
        String transId = request.getHeader(TRANSID);
        if (StringUtils.isEmpty(transId)) {
            transId = (String) request.getAttribute(TRANSID);
            if (StringUtils.isEmpty(transId)) {
                transId = NoUtil.getNo();
            }
        }
        request.setAttribute(TRANSID, transId);
        MDC.put(TRANSID, transId);
    }


}
